package com.zb.zeus.common.watermark.utils;

import com.zb.zeus.common.watermark.config.pdfbox.ImageConfig;
import com.zb.zeus.common.watermark.config.pdfbox.TextConfig;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
import org.apache.pdfbox.util.Matrix;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;

/**
 * pdf水印工具类
 * <p>
 *     页面宽高
 *     from :https://cloud.tencent.com/developer/ask/103371
 *     pdfbox获取一页宽高
 *     page.getMediaBox().getHeight() // 842.0
 *     page.getMediaBox().getWidth() // 595.22
 *     Adobe使用以下定义：1 pt = 1/72 inch
 *     所以转换毫米：mm = pt*25.4 / 72
 * </p>
 *
 * <p>
 *     字符宽度（pt）计算
 *     from https://zhuanlan.zhihu.com/p/52863069
 *     通过在Java JDK中Graphics2D
 *     public static int calCharactersWidth(Graphics2D g, Font font, String text) {
 *      FontRenderContext context = g.getFontRenderContext();
 *          // 获取字体的像素范围对象
 *      Rectangle2D stringBounds = font.getStringBounds(text, context);
 *      int fontWidth = (int)stringBounds.getWidth();
 *      return fontWidth;
 *      }
 *
 *      通过PDFBox字体类计算字符占位宽度（pt）
 *      float realWidth = FONT_SIZE * FONT.getStringWidth(tmpText) / 1000;
 * </p>
 *
 * <p>
 *     pdfbox自带的字体不支持中文，所以这里要引入语言支持比较多的第三方字体`arial`
 * </p>
 *
 * @author Chenpeng.Huang
 * @date 2021/07/20 09:38
 **/
public class PdfWaterMarkUtil {
    /**
     * 添加文字水印
     * @param config 配置
     * @throws IOException io异常
     */
    public static void markText(TextConfig config) throws IOException {
        File targetPdf = new File(config.getTargetPath());
        PDDocument doc = PDDocument.load(new File(config.getSourcePath()));
        doc.setAllSecurityToBeRemoved(true);
        InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("ttf/arial_unicode_ms.ttf");
        PDFont pdFontUnicode = PDType0Font.load(doc, is);
        // 获取字段长度
        float textWidth = config.getFontSize() * pdFontUnicode.getStringWidth(config.getText()) / 1000.0f;
        // 旋转的角度值
        double rotation = Math.toRadians(config.getRotationAngel());
        // 字体占位矩形对角线长度
        double diagonal = Math.sqrt(Math.pow(textWidth, 2) + Math.pow(config.getFontSize(), 2));
        // 旋转后换算的单位宽度
        float itemWidth = (float) (Math.abs(diagonal * Math.cos(rotation)) + config.getFontSize() + config.getMarginWidth());
        // 旋转后换算的单位高度
        float itemHeight = (float) (Math.abs(diagonal * Math.sin(rotation)) + config.getFontSize() + config.getMarginHeight());

        for (PDPage page : doc.getPages()) {
            float pageHeight = page.getMediaBox().getHeight();
            float pageWidth = page.getMediaBox().getWidth();
            // 当存在旋转和缩放操作时，resetContext最好是true，否则上下文不好管理
            PDPageContentStream cs = new PDPageContentStream(doc, page, PDPageContentStream.AppendMode.APPEND, true,
                    true);
            PDExtendedGraphicsState r0 = new PDExtendedGraphicsState();
            // 透明度
            r0.setNonStrokingAlphaConstant(config.getAlpha());
            r0.setAlphaSourceFlag(true);
            cs.setGraphicsStateParameters(r0);
            // RGB
            cs.setNonStrokingColor(config.getR()/255.0f, config.getG()/255.0f, config.getB()/255.0f);
            cs.setFont(pdFontUnicode, config.getFontSize());

            if (config.getTiled()){
                // 平铺
                float w =  - itemWidth;
                float totalW = pageWidth + itemWidth;
                float totalH = pageHeight + itemHeight;
                while (w < totalW){
                    float h = - itemHeight;
                    while (h < totalH){
                        cs.beginText();
                        // 获取旋转实例
                        cs.setTextMatrix(Matrix.getRotateInstance(rotation, w, h));
                        cs.showText(config.getText());
                        cs.endText();
                        h += itemHeight;
                    }
                    w += itemWidth;
                }
            }else {
                cs.beginText();
                // 获取旋转实例
                cs.setTextMatrix(Matrix.getRotateInstance(rotation, (pageWidth + textWidth)/2.0f, pageHeight/2.0f));
                cs.showText(config.getText());
                cs.endText();
            }
            cs.close();
        }
        doc.save(targetPdf);
        doc.close();
    }

    /**
     * 添加文字水印
     * @param config 配置
     * @throws IOException io异常
     */
    public static void markImage(ImageConfig config) throws IOException {
        File targetPdf = new File(config.getTargetPath());
        PDDocument doc = PDDocument.load(new File(config.getSourcePath()));
        doc.setAllSecurityToBeRemoved(true);

        PDImageXObject pdImage = PDImageXObject.createFromFile(config.getImagePath(), doc);
        // 旋转的角度值
        double rotation = Math.toRadians(config.getRotationAngel());

        float itemWidth = (float) (pdImage.getWidth()* (Math.abs(Math.cos(rotation))) + pdImage.getHeight() * (Math.abs(Math.sin(rotation))) + config.getMarginWidth());
        float itemHeight = (float) (pdImage.getWidth()* (Math.abs(Math.sin(rotation))) + pdImage.getHeight() * (Math.abs(Math.cos(rotation))) + config.getMarginHeight());

        for (PDPage page : doc.getPages()) {
            float pageHeight = page.getMediaBox().getHeight();
            float pageWidth = page.getMediaBox().getWidth();
            PDPageContentStream cs = new PDPageContentStream(doc, page, PDPageContentStream.AppendMode.APPEND, true,
                    true);
            PDExtendedGraphicsState r0 = new PDExtendedGraphicsState();
            // 透明度
            r0.setNonStrokingAlphaConstant(config.getAlpha());
            r0.setAlphaSourceFlag(true);
            cs.setGraphicsStateParameters(r0);

            if (config.getTiled()){
                // 平铺
                float w =  - itemWidth;
                float totalW = pageWidth + itemWidth;
                float totalH = pageHeight + itemHeight;
                while (w < totalW){
                    float h = - itemHeight;
                    while (h < totalH){
                        cs.saveGraphicsState();
                        cs.transform(Matrix.getRotateInstance(rotation, w, h));
                        cs.drawImage(pdImage, 0, 0);
                        cs.restoreGraphicsState();
                        h += itemHeight;
                    }
                    w += itemWidth;
                }
            }else {
                cs.saveGraphicsState();
                cs.transform(Matrix.getRotateInstance(rotation, (pageWidth - pdImage.getWidth())/2, (pageHeight - pdImage.getHeight())/2));
                cs.drawImage(pdImage, 0, 0);
                cs.restoreGraphicsState();
            }

            cs.close();
        }

        doc.save(targetPdf);
        doc.close();
    }
}
